package cn.parser;

import cn.dao.NeoDao;
import cn.dao.impl.NeoDaoImpl;
import cn.structure.*;
import cn.utils.NeoUtil;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;

/**
 * Neo4j 解析器，将实体映射为节点和关系
 *
 * @author Tao Lu
 */
public class NeoParser {
    private static NeoParser instance = new NeoParser();
    private static GraphDatabaseService service = NeoUtil.getService();
    private static NeoDao dao = NeoDaoImpl.getInstance();

    public static NeoParser getInstance() {
        return instance;
    }

    /**
     * 解析后使用，通过 spoonParser 引用的节点实体、方法实体创建图
     *
     * @param spoonParser 解析管理器实例
     */
    public void createGraph(SpoonParser spoonParser) {
        try (Transaction tx = service.beginTx()) {
            spoonParser.getClassEntities().forEach(entity -> dao.createClassNode(tx, entity));
            spoonParser.getExecutableEntities().forEach(entity -> {
                if (entity instanceof MethodEntity) {
                    dao.createMethodNode(tx, (MethodEntity) entity);
                } else if (entity instanceof ConstructorEntity) {
                    dao.createConstructorNode(tx, (ConstructorEntity) entity);
                }
            });
            spoonParser.getAbstractInvocationEntities().forEach(entity -> {
                if (entity instanceof InvocationEntity) {
                    dao.createInvocationNode(tx, (InvocationEntity) entity);
                } else if (entity instanceof ConstructorCallEntity) {
                    dao.createConstructorCallNode(tx, (ConstructorCallEntity) entity);
                }
            });
            spoonParser.getTypeParameterEntities().forEach(entity -> dao.createTypeParameterNode(tx, entity));
            spoonParser.getVariableEntities().forEach(entity -> dao.createVariableNode(tx, entity));
            spoonParser.getRelationEntities().forEach(relationEntity -> {
                BaseEntity source_entity = relationEntity.getSource();
                BaseEntity target_entity = relationEntity.getTarget();
                Node source_node = dao.getNodeByAstPath(tx, source_entity.getEntityType(), source_entity.getAst_path());
                Node target_node = dao.getNodeByAstPath(tx, target_entity.getEntityType(), target_entity.getAst_path());
                dao.createRelation(source_node, target_node, relationEntity.getRelation_type());
            });
            tx.commit();
        }
    }
}
